package wang.raye.springcloud.zuul;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.core.Ordered;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Created by Raye on 2018-06-12
 */
public class MyRouteLocator implements RouteLocator, Ordered {
    private static final Log log = LogFactory.getLog(SimpleRouteLocator.class);
    private static final int DEFAULT_ORDER = 0;
    private ZuulProperties properties;
    private PathMatcher pathMatcher = new AntPathMatcher();
    private String dispatcherServletPath = "/";
    private String zuulServletPath;
    private AtomicReference<Map<String, ZuulProperties.ZuulRoute>> routes = new AtomicReference();
    private int order = 0;
    private HttpServletRequest request;

    public MyRouteLocator(String servletPath, ZuulProperties properties,HttpServletRequest request) {
        this.properties = properties;
        if (StringUtils.hasText(servletPath)) {
            this.dispatcherServletPath = servletPath;
        }

        this.zuulServletPath = properties.getServletPath();
        this.request = request;
    }

    public List<Route> getRoutes() {
        if (this.routes.get() == null) {
            this.routes.set(this.locateRoutes());
        }

        List<Route> values = new ArrayList();
        Iterator var2 = ((Map)this.routes.get()).keySet().iterator();

        while(var2.hasNext()) {
            String url = (String)var2.next();
            ZuulProperties.ZuulRoute route = (ZuulProperties.ZuulRoute)((Map)this.routes.get()).get(url);
            String path = route.getPath();
            values.add(this.getRoute(route, path));
        }

        return values;
    }

    public Collection<String> getIgnoredPaths() {
        return this.properties.getIgnoredPatterns();
    }

    public Route getMatchingRoute(String path) {
        if (log.isDebugEnabled()) {
            log.debug("Finding route for path: " + path);
        }

        if (this.routes.get() == null) {
            this.routes.set(this.locateRoutes());
        }

        if (log.isDebugEnabled()) {
            log.debug("servletPath=" + this.dispatcherServletPath);
            log.debug("zuulServletPath=" + this.zuulServletPath);
            log.debug("RequestUtils.isDispatcherServletRequest()=" + RequestUtils.isDispatcherServletRequest());
            log.debug("RequestUtils.isZuulServletRequest()=" + RequestUtils.isZuulServletRequest());
        }

        String adjustedPath = this.adjustPath(path);
        ZuulProperties.ZuulRoute route = null;
        if (!this.matchesIgnoredPatterns(adjustedPath)) {
            Iterator var4 = ((Map)this.routes.get()).entrySet().iterator();

            while(var4.hasNext()) {
                Map.Entry<String, ZuulProperties.ZuulRoute> entry = (Map.Entry)var4.next();
                String pattern = (String)entry.getKey();
                log.debug("Matching pattern:" + pattern);

                if (this.pathMatcher.match(pattern, "/"+request.getParameter("name"))) {
                    route = (ZuulProperties.ZuulRoute)entry.getValue();
                    break;
                }
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("route matched=" + route);
        }

        return this.getRoute(route, adjustedPath);
    }

    private Route getRoute(ZuulProperties.ZuulRoute route, String path) {
        if (route == null) {
            return null;
        } else {
            String targetPath = path;
            String prefix = "/raye";
            if (path.startsWith(prefix) && this.properties.isStripPrefix()) {
                targetPath = path.substring(prefix.length());
            }

//            if (route.isStripPrefix()) {
//                int index = route.getPath().indexOf("*") - 1;
//                if (index > 0) {
//                    String routePrefix = route.getPath().substring(0, index);
//                    targetPath = targetPath.replaceFirst(routePrefix, "");
//                    prefix = prefix + routePrefix;
//                }
//            }

            Boolean retryable = this.properties.getRetryable();
            if (route.getRetryable() != null) {
                retryable = route.getRetryable();
            }

            return new Route(route.getId(), targetPath, route.getLocation(), prefix, retryable, route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null);
        }
    }

    protected void doRefresh() {
        this.routes.set(this.locateRoutes());
    }

    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap();
        Iterator var2 = this.properties.getRoutes().values().iterator();

        while(var2.hasNext()) {
            ZuulProperties.ZuulRoute route = (ZuulProperties.ZuulRoute)var2.next();
            routesMap.put(route.getPath(), route);
        }

        return routesMap;
    }

    protected boolean matchesIgnoredPatterns(String path) {
        Iterator var2 = this.properties.getIgnoredPatterns().iterator();

        String pattern;
        do {
            if (!var2.hasNext()) {
                return false;
            }

            pattern = (String)var2.next();
            log.debug("Matching ignored pattern:" + pattern);
        } while(!this.pathMatcher.match(pattern, path));

        log.debug("Path " + path + " matches ignored pattern " + pattern);
        return true;
    }

    private String adjustPath(String path) {
        String adjustedPath = path;
        if (RequestUtils.isDispatcherServletRequest() && StringUtils.hasText(this.dispatcherServletPath)) {
            if (!this.dispatcherServletPath.equals("/")) {
                adjustedPath = path.substring(this.dispatcherServletPath.length());
                log.debug("Stripped dispatcherServletPath");
            }
        } else if (RequestUtils.isZuulServletRequest() && StringUtils.hasText(this.zuulServletPath) && !this.zuulServletPath.equals("/")) {
            adjustedPath = path.substring(this.zuulServletPath.length());
            log.debug("Stripped zuulServletPath");
        }

        log.debug("adjustedPath=" + adjustedPath);
        return adjustedPath;
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}
